import pandas as pd
import numpy as np
from scipy.sparse import csr_matrix
from sklearn.cluster import MiniBatchKMeans
from sklearn.preprocessing import RobustScaler
from mlxtend.frequent_patterns import apriori, association_rules
import plotly.express as px
import plotly.graph_objects as go
from plotly.io import write_html
from concurrent.futures import ThreadPoolExecutor
import warnings
from pathlib import Path
import os
import sys
import webbrowser
from typing import Dict, Any

# 环境配置
print(f"Python路径: {sys.executable}")
print(f"Pandas版本: {pd.__version__}")
print(f"工作目录: {os.getcwd()}")
warnings.filterwarnings('ignore')
pd.set_option('display.max_columns', None)
px.set_mapbox_access_token(open('.mapbox_token').read() if os.path.exists('.mapbox_token') else None)


# ==================== 1. 数据加载 ====================
def load_data(path: Path) -> pd.DataFrame:
    """并行分块加载+智能时间过滤"""
    if not path.exists():
        available_files = '\n'.join([f.name for f in path.parent.iterdir()])
        raise FileNotFoundError(f"文件不存在: {path}\n目录下现有文件:\n{available_files}")

    dtypes = {
        'user_id': 'uint32',
        'item_id': 'uint32',
        'category_id': 'uint16',
        'behavior': 'category',
        'timestamp': 'int64'
    }

    def process_chunk(chunk):
        chunk['timestamp'] = pd.to_datetime(chunk['timestamp'], unit='s', errors='coerce')
        return chunk[chunk['timestamp'].between('2000-01-01', '2023-12-31')].copy()

    try:
        with ThreadPoolExecutor(max_workers=os.cpu_count()) as executor:
            chunks = pd.read_csv(
                path,
                names=list(dtypes.keys()),
                dtype=dtypes,
                chunksize=5_000_000,
                parse_dates=False,
                low_memory=False
            )
            dfs = list(executor.map(process_chunk, chunks))

        df = pd.concat(dfs, ignore_index=True)
        print(f"✅ 数据加载完成 | 记录数: {len(df):,} | 内存用量: {df.memory_usage(deep=True).sum() / 1024 ** 2:.2f} MB")
        return df
    except Exception as e:
        print(f"❌ 数据加载失败: {str(e)}")
        raise


# ==================== 2. 优化版漏斗分析 ====================
def fast_funnel(df: pd.DataFrame) -> Dict[str, Any]:
    """返回分析结果和图表对象"""
    behavior_counts = df['behavior'].value_counts()
    pv = round(behavior_counts.get('pv', 0) / 10000, 1)
    cart = round(behavior_counts.get('cart', 0) / 10000, 1)
    buy = round(behavior_counts.get('buy', 0) / 10000, 1)

    fig = go.Figure(go.Funnel(
        y=["浏览用户", "加购用户", "支付用户"],
        x=[pv, cart, buy],
        texttemplate="<b>%{y}</b><br>数量: %{x:.1f}万人<br>转化率: %{percentPrevious:.1%}",
        textposition="inside",
        textfont={"size": 14, "color": "white", "family": "Microsoft YaHei"},
        marker={
            "color": ["#4285F4", "#FBBC05", "#34A853"],
            "line": {"width": 2, "color": "white"}
        },
        connector={"line": {"color": "#EA4335", "width": 2}}
    ))

    fig.update_layout(
        title={
            'text': "<b>用户行为漏斗分析（单位：万人）</b>",
            'y': 0.95,
            'x': 0.5,
            'font': {'size': 24, 'family': 'Microsoft YaHei'}
        },
        margin={"t": 100, "b": 50, "l": 50, "r": 200},
        height=600,
        plot_bgcolor="white"
    )

    return {'pv': pv, 'cart': cart, 'buy': buy, 'figure': fig}


# ==================== 3. RFM分析 ====================
def turbo_rfm(df: pd.DataFrame, sample_frac: float = 0.3) -> pd.DataFrame:
    rfm = (
        df[df['behavior'] == 'buy']
        .sample(frac=sample_frac, random_state=42)
        .groupby('user_id')
        .agg(
            Recency=('timestamp', lambda x: (pd.Timestamp.now() - x.max()).days),
            Frequency=('item_id', 'count'),
            Monetary=('item_id', 'sum')
        )
        .query('Monetary > 0')
    )

    scaler = RobustScaler()
    features = scaler.fit_transform(rfm)

    kmeans = MiniBatchKMeans(n_clusters=4, batch_size=1024, random_state=42, n_init='auto')
    rfm['cluster'] = kmeans.fit_predict(features)
    rfm['cluster'] = rfm['cluster'].astype('category')

    return rfm


def plot_rfm_3d(rfm: pd.DataFrame) -> go.Figure:
    rfm_vis = rfm.copy()
    rfm_vis['Monetary'] = rfm_vis['Monetary'] / 10000

    fig = px.scatter_3d(
        rfm_vis,
        x='Recency',
        y='Frequency',
        z='Monetary',
        color='cluster',
        opacity=0.7,
        size_max=10,
        hover_name=rfm_vis.index,
        title='RFM客户分群分析',
        labels={
            'Recency': '最近消费时间(天)',
            'Frequency': '消费频次',
            'Monetary': '消费金额(万元)',
            'cluster': '客户分群'
        }
    )

    fig.update_layout(
        margin=dict(l=0, r=0, b=0, t=30),
        font=dict(family="Microsoft YaHei", size=12)
    )

    return fig


# ==================== 4. 关联规则分析 ====================
def mega_association_rules(df: pd.DataFrame, top_items: int = 1000) -> pd.DataFrame:
    buy_df = df[df['behavior'] == 'buy'].copy()

    item_popularity = buy_df.drop_duplicates(['user_id', 'item_id'])['item_id'].value_counts()
    top_items = item_popularity.head(top_items).index
    filtered = buy_df[buy_df['item_id'].isin(top_items)].copy()

    user_item = (
        filtered.drop_duplicates(['user_id', 'item_id'])
        .groupby(['user_id', 'item_id']).size()
        .unstack()
        .fillna(0)
        .astype(bool)
    )

    freq_items = apriori(user_item, min_support=0.01, use_colnames=True, low_memory=True)

    if not freq_items.empty:
        rules = association_rules(freq_items, metric='lift', min_threshold=1)
        return rules.sort_values('lift', ascending=False)

    return pd.DataFrame()


# ==================== 5. 加购用户分析 ====================
def analyze_cart_users(df: pd.DataFrame) -> Dict[str, Any]:
    cart_users = df[df['behavior'] == 'cart']['user_id'].unique()
    buy_users = df[df['behavior'] == 'buy']['user_id'].unique()
    cart_only_users = set(cart_users) - set(buy_users)

    cart_items = df[
        (df['user_id'].isin(cart_only_users)) &
        (df['behavior'] == 'cart')
        ].groupby('item_id').size().sort_values(ascending=False)

    fig = None
    if not cart_items.empty:
        top_items = cart_items.head(10)
        fig = px.bar(
            x=top_items.index.astype(str),
            y=top_items.values,
            title='加购未购买商品TOP10',
            labels={'x': '商品ID', 'y': '加购次数'},
            color=top_items.values,
            color_continuous_scale='Viridis'
        )
        fig.update_layout(
            font=dict(family="Microsoft YaHei"),
            xaxis_tickangle=-45
        )

    return {
        'cart_only_users': len(cart_only_users),
        'top_cart_items': cart_items.head(5).to_dict(),
        'figure': fig
    }


# ==================== 6. 报告生成 ====================
def generate_html_report(data_path: Path, results: Dict[str, Any]) -> str:
    """生成完整的HTML分析报告"""
    output_path = data_path.parent / "analysis_report.html"

    # 准备图表HTML
    charts_html = []
    for chart_name in [
        ('用户行为漏斗', results['funnel']['figure']),
        ('加购分析', results['cart']['figure']),
        ('RFM分析', results['rfm_figure'])
    ]:
        if chart_name[1] is not None:
            charts_html.append(f"""
            <div class="chart">
                <h2>{chart_name[0]}</h2>
                {chart_name[1].to_html(full_html=False, include_plotlyjs='cdn')}
            </div>
            """)

    # 关联规则表格
    rules_html = ""
    if not results['rules'].empty:
        rules_df = results['rules'].head(10)
        rules_html = f"""
        <div class="chart">
            <h2>强关联规则TOP10</h2>
            {rules_df.to_html(classes='dataframe', border=0)}
        </div>
        """

    # 组合完整HTML
    full_html = f"""
    <!DOCTYPE html>
    <html>
    <head>
        <title>电商用户行为分析报告</title>
        <meta charset="utf-8">
        <style>
            body {{ font-family: Microsoft YaHei, sans-serif; margin: 20px; }}
            .chart {{ margin: 30px 0; border-bottom: 1px solid #eee; padding-bottom: 30px; }}
            h1 {{ color: #333; text-align: center; }}
            h2 {{ color: #4285F4; margin-top: 40px; }}
            .dataframe {{
                width: 100%;
                border-collapse: collapse;
                margin: 15px 0;
            }}
            .dataframe th, .dataframe td {{
                padding: 8px;
                text-align: left;
                border-bottom: 1px solid #ddd;
            }}
            .dataframe tr:nth-child(even) {{ background-color: #f2f2f2; }}
        </style>
    </head>
    <body>
        <h1>电商用户行为分析报告</h1>
        {"".join(charts_html)}
        {rules_html}
    </body>
    </html>
    """

    with open(output_path, "w", encoding="utf-8") as f:
        f.write(full_html)

    return str(output_path)


# ==================== 主程序 ====================
if __name__ == "__main__":
    try:
        # 配置路径
        data_path = Path(r"E:\zhuo_mian\数据分析\项目\电商用户行为分析\UserBehavior.csv")

        # 1. 加载数据
        print("🔄 数据加载中...")
        df = load_data(data_path)
        results = {}

        # 2. 漏斗分析
        print("\n⚡ 漏斗分析中...")
        results['funnel'] = fast_funnel(df)

        # 3. 加购分析
        print("\n🛒 加购用户分析中...")
        results['cart'] = analyze_cart_users(df)

        # 4. RFM分析
        print("\n🌀 RFM分群中...")
        rfm = turbo_rfm(df, sample_frac=0.2)
        results['rfm_figure'] = plot_rfm_3d(rfm)

        # 5. 关联规则
        print("\n🔗 关联规则挖掘中...")
        buy_df = df[df['behavior'] == 'buy']
        results['rules'] = pd.DataFrame()
        if not buy_df.empty:
            results['rules'] = mega_association_rules(buy_df)

        # 6. 生成报告
        print("\n📊 生成分析报告中...")
        report_path = generate_html_report(data_path, results)
        print(f"✅ 分析完成！报告已保存至: {report_path}")

        # 自动打开报告
        webbrowser.open(report_path)

    except Exception as e:
        print(f"\n❌ 程序执行失败: {str(e)}")
        if isinstance(e, FileNotFoundError):
            print("请检查路径:", data_path)